package top.vains.config.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import top.vains.entity.Permission;
import top.vains.service.IPermissionService;

import javax.servlet.Filter;
import java.util.*;

/**
 * Shiro配置类
 *
 * @author vains
 * @version 1.0
 * @since 2020/2/18 15:49
 **/
@Slf4j
@Configuration
public class ShiroConfiguration {

    /**
     * 定义shiro过滤器拦截规则
     * @param securityManager 安全管理器
     * @return 返回shiro过滤器工厂
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean
                                    (SecurityManager securityManager,
                                     IPermissionService permissionService) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 需要拦截的权限的容器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置不会被拦截的请求路径
        filterChainDefinitionMap.put("/login/**", "anon");
        // 添加过滤器
        Map<String, Filter> filterMap = new HashMap<>(1);
        filterMap.put("jwt", new JwtFilter());
        filterMap.put("perms", new AuthorizationFilter());
        // 添加自己的过滤器
        shiroFilterFactoryBean.setFilters(filterMap);
        // 需要控制的权限
        List<Permission> permissions = permissionService.selectPermissions();
        for (Permission permission : permissions) {
            if (!Objects.equals(permission.getAuthorization(), "anon")) {
                String perms = "jwt,perms[" +
                        permission.getAuthorization() +
                        "]";
                filterChainDefinitionMap.put(permission.getPageUrl(), perms);
            }
        }
        // 过滤链定义，从上向下顺序执行，一般将/**放在最下边
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 配置安全管理器
     * @param userRealm 自定义realm
     * @return 安全管理器
     */
    @Bean("securityManager")
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        // 关闭shiro自带的session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);
        return securityManager;
    }

    /**
     * 将自定义realm注入ioc
     */
    @Bean("userRealm")
    public UserRealm userRealm() {
        return new UserRealm();
    }

    /**
     * 自动创建代理，没有这个鉴权可能会报错
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        autoProxyCreator.setProxyTargetClass(true);
        return autoProxyCreator;
    }

    /**
     * 开启shiroAOP支持
     * 使用代理方式，需要开启代码支持
     * @param securityManager 安全管理器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        sourceAdvisor.setSecurityManager(securityManager);
        return sourceAdvisor;
    }

    /**
     * 禁用session，不保存登陆状态，每次请求都要重新验证
     * 如果代码里调用subject.getSession()还是可以使用session的
     *
     */
    @Bean
    public SessionStorageEvaluator sessionStorageEvaluator() {
        DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        return sessionStorageEvaluator;
    }

}
